#pragma once
/* Copyright (C) 2009 Vlad Roubtsov. All rights reserved. */

#include <typeinfo>
#include <vector>
using std::vector;

#include <boost/ptr_container/ptr_vector.hpp>
using boost::ptr_vector;
#include <boost/variant.hpp>

#include "vr/deject/component.hpp"
#include "vr/deject/types.hpp"
#include "vr/deject/factory/value.hpp"
#include "vr/util/assertions.hpp"
#include "vr/util/logger.hpp"
#include "vr/util/utility.hpp"

//----------------------------------------------------------------------------
namespace vr
{
namespace deject
{
namespace factory
{

using deject::component_base;

class dependency: noncopyable
{
    public: // ...............................................................


        typedef vector<component_base *> seq_of_ref_type; // don't use lists for extra mem locality
        typedef component_base * creator_t (const deject::component_id_type & ID, const ptr_vector<dependency> & dependencies);


        const deject::dep_value_type::enum_t m_type;


        dependency (component_base * const c) :
            m_type (deject::dep_value_type::REFERENCE),
            m_value (c)
        {
        }

        dependency (const vector<component_base *> & cs) :
            m_type (deject::dep_value_type::SEQ_OF_REFERENCE),
            m_value (cs)
        {
        }

        dependency (const value & v) :
            m_type (deject::dep_value_type::VALUE),
            m_value (v)
        {
        }

        dependency (const value::map_of_val_type & vs) :
            m_type (deject::dep_value_type::MAP_OF_VALUE),
            m_value (vs)
        {
        }


        template<typename T>
        operator T & () const
        {
            VR_SASSERT (m_type == deject::dep_value_type::REFERENCE);

            component_base * const c = boost::get<component_base *> (m_value);
            T * const cT = dynamic_cast<T *> (c);

            VR_SASSERT (cT);
            return * cT;
        }

        // note: avoid general templatized constructors/conversion operators
        // (these tend to cause resolution ambiguities, esp. the latter)

#define __VR_DEFINE_CONVERSION(T) \
        \
        operator T () const\
        {\
            VR_SASSERT (m_type == deject::dep_value_type::VALUE);\
            return static_cast< T > (boost::get<value> (m_value));\
        }

        __VR_DEFINE_CONVERSION (double)
        __VR_DEFINE_CONVERSION (int64_t)
        __VR_DEFINE_CONVERSION (int32_t)
        __VR_DEFINE_CONVERSION (int16_t)
        __VR_DEFINE_CONVERSION (int8_t)
        __VR_DEFINE_CONVERSION (bool)

#undef __VR_DEFINE_CONVERSION

    private: // ..............................................................

        typedef boost::variant
            <
                value::map_of_val_type,
                value,
                component_base *,
                seq_of_ref_type
            >
        dep_type;


        const dep_type m_value;

}; // end of nested class

} // end of 'factory'
} // end of 'deject'
} // end of namespace
//----------------------------------------------------------------------------
